/*
 * Filename : DataHandler.java
 * Description : **COMPLETE**
 * History  : Date               Engineer       Description
 *            ---------          ------------   --------------------------------
 *            August 24, 2007      waitek         Initial creation.
 */
package com.lct.eq2.data;

import java.util.ArrayList;
import com.lct.eq2.gui.LiteGui;
import com.lct.eq2.gui.TimerWindow;
import com.lct.eq2.tail.Tail;
import com.lct.eq2.watchers.EncounterWatcher;
import com.lct.eq2.watchers.ExclusionWatcher;
import com.sun.org.apache.xpath.internal.XPathAPI;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class DataHandler {
    public LiteGui parent;
    private EncounterWatcher watcher = null;
    private Encounter currentEncounter = null;
    private ArrayList combatLines = null;
    private boolean encounterStartedWithHeal = false;
    private int logType;
    private Tail parentTail;
    private TimerWindow timerParent;
    private ArrayList exclusionPeriod = null;
    private ArrayList spells = null;
    private HashMap spellList = null;
    

    /**
     * Creates a new instance of DataHandler
     */
    public DataHandler() {
        exclusionPeriod = new ArrayList();
        spellList = new HashMap();
        spells = new ArrayList();
        logType = 1;
        combatLines = new ArrayList();
        initCombatTypes();
        loadSpellTimerInfo();
    }

    /**
     * Creates a new instance of DataHandler
     */
    public DataHandler(LiteGui parent, int logType, Tail parentTail) {
        exclusionPeriod = new ArrayList();
        spellList = new HashMap();
        spells = new ArrayList();
        this.parent = parent;
        this.logType = logType;
        combatLines = new ArrayList();
        initCombatTypes();
        this.parentTail = parentTail;
        loadSpellTimerInfo();
    }

    /**
     * Creates a new instance of DataHandler
     */
    public DataHandler(LiteGui parent) {
        exclusionPeriod = new ArrayList();
        spellList = new HashMap();
        spells = new ArrayList();
        logType = 1;
        this.parent = parent;
        combatLines = new ArrayList();
        initCombatTypes();
        loadSpellTimerInfo();
    }

    private void loadSpellTimerInfo() {
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(new FileInputStream(new File("timers.xml")));

            NodeList dataItems = XPathAPI.selectNodeList(doc, "Config/SpellTimers/Spell");
            for (int i = 0; i < dataItems.getLength(); i++) {
                Element element = (Element)dataItems.item(i);
                String[] tmp = {element.getAttribute("Name"),
                                element.getAttribute("Timer"),
                                element.getAttribute("Checked"),
                                (element.getAttribute("DoT") != null ? element.getAttribute("DoT") : "false")};
                spellList.put(tmp[0], tmp);
                spells.add(tmp[0]);
//                System.out.println(tmp[0] + ", " + tmp[1]);
            }
            //System.out.println("Spell timers loaded.");
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        } catch (ParserConfigurationException ex) {
            ex.printStackTrace();
        } catch (SAXException ex) {
            ex.printStackTrace();
        } catch (TransformerException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        this.parent = parent;
    }

    public void setTimerParent(TimerWindow timerParent) {
        this.timerParent = timerParent;
    }

    private void addSpellToTimerWindow(Attack attack) {
        String attackName = attack.getAttackName();
        if (!this.exclusionPeriod.contains(attackName)) {
            if (this.spells.contains(attackName)) {
                String[] info = (String[])this.spellList.get(attackName);
//                System.out.println(attackName + " AOE hit");
                
                if (Boolean.parseBoolean(info[3])) {
                    if (this.currentEncounter.getAssumedMainTank().getName().equalsIgnoreCase(attack.getTarget())) {
                        System.out.println("  " + attackName + " has hit the MT (" + currentEncounter.getAssumedMainTank().getName() + ")");
                        double mult = 1;
//                        if (this.currentEncounter.getCombatant(attack.getAttacker()).hasTraumaticSwipe()) {
//                            mult = 1.5;
//                        }
                        //System.out.println(attackName + " just hit.");
                        this.parent.addSpellToTimerWindow(this.spellList, attackName, mult);
                        this.exclusionPeriod.add(attackName);
                        ExclusionWatcher exclusionWatcher = new ExclusionWatcher(this, attackName);
                    } else {
                        System.out.println("  " + attackName + " did NOT hit the MT, it hit " + attack.getTarget());
                    }
                } else {
                    double mult = 1;
                    if (this.currentEncounter != null && attack.getAttacker() != null) {
//                        if (this.currentEncounter.getCombatant(attack.getAttacker()).hasTraumaticSwipe()) {
//                            mult = 1.5;
//                        }
                        System.out.println(attackName + " AOE hit");
                        this.parent.addSpellToTimerWindow(this.spellList, attackName, mult);
                        this.exclusionPeriod.add(attackName);
                        ExclusionWatcher exclusionWatcher = new ExclusionWatcher(this, attackName);
                    }
                }
            }
        }
    }

    public void removeFromExclusionPeriod(String attackName) {
        this.exclusionPeriod.remove(attackName);
//        System.out.println("Spells in Exclusion Period:");
//        for (int x = 0; x < exclusionPeriod.size(); x++) {
//            System.out.println("  " + exclusionPeriod.get(x));
//        }
    }

    public void setParentGui(LiteGui parent) {
        this.parent = parent;
    }

    public void addObjectToEncounter(Object o) {
        long lagBefore = System.currentTimeMillis();
        if (o instanceof Encounter) {
            Encounter e = (Encounter)o;
            this.currentEncounter.addEncounter(e, e.getEncounterDuration());
        } else if (o instanceof Line) {
            Line line = (Line)o;
            long timeStamp = Long.parseLong(line.getLine().substring(1, 11));
            long serverTime = line.getLocalTime();
            long timeDelta = System.currentTimeMillis() - serverTime;
            long newTimeStamp = ((long) ((timeStamp * 1000) - timeDelta + line.getTimeDelta()) / 1000);
            line.setLine(line.getLine().replaceAll(timeStamp + "", newTimeStamp + ""));
            //System.out.println(line.getLine());
            this.addData(line.getLine(), false, false, line.getCharacter());
        } else if (o instanceof String) {
            //System.out.println(o);
        } else if (o instanceof Attack) {
            
            Attack a = (Attack)o;
            long currentTime = System.currentTimeMillis();

//            if (this.parent.getMainCharacter().equalsIgnoreCase("Moardots")) {
//                currentTime -= deltaOffset;
//            }
//            a.setLocalDelta(currentTime - a.getLocalDelta());
            long timeStamp = a.getTimeStamp();
//            System.out.println("DataHandler::addObjectToEncounter() - attack timeStamp: " + timeStamp + " (" + new Date(timeStamp) + ")");
            long remoteTime = a.getLocalTime();
//            System.out.println("DataHandler::addObjectToEncounter() - Remote Client Time: " + remoteTime + " (" + new Date(remoteTime) + ")");
            long serverTime = a.getServerTime();
//            System.out.println("DataHandler::addObjectToEncounter() - Server Time: " + serverTime + " (" + new Date(serverTime) + ")");
//            System.out.println("DataHandler::addObjectToEncounter() - Local Time: " + currentTime + " (" + new Date(currentTime) + ")");
            double remoteDelta = a.getRemoteDelta();
//            System.out.println("DataHandler::addObjectToEncounter() - remoteDelta: " + remoteDelta);
            double localDelta = serverTime - currentTime;
//            System.out.println("DataHandler::addObjectToEncounter() - localDelta: " + localDelta);
            double finalDelta = localDelta - remoteDelta;
//            System.out.println("DataHandler::addObjectToEncounter() - finalDelta: " + finalDelta);

            long newTimeStamp = (long) (timeStamp - finalDelta - (System.currentTimeMillis() - lagBefore));// - a.getLagTime());

            System.out.println("DataHandler::addObjectToEncounter() - newTimeStamp: " + newTimeStamp + " (" + new Date(newTimeStamp) + ")");

            a.setTimeStamp(newTimeStamp);
            this.addData(a, a.getMainCharacter());
        } else {
            System.out.println(o.getClass().getName());
        }
    }

    public int checkForCombatLine(String line, boolean propagated) {
        line = line.trim();
        int rval = -1;

//        if (line.contains("(1280373705)[Wed Jul 28 23:21:45 2010] Azara the Seer's Brutal Inquiry hits Yiix for 422")) {
//            boolean testing = true;
//        }

        boolean verifier = true;
        boolean _return = false;

        String _line = null;
        String[] split = null;
        String[] tokens = null;
        int z = 0;
        try {
            while ((z < combatLines.size()) && (_return == false)) {
                verifier = true;
                rval = -1;
                _line = (String) combatLines.get(z);
                z++;
                split = _line.split("::");
                tokens = (split[0]).split(";;");
                for (int x = 0; x < tokens.length; x++) {
                    if ((line.contains(tokens[x])) && (verifier)) {
                        verifier = true;
                        rval = Integer.parseInt(split[1]);
                    } else {
                        verifier = false;
                        rval = -1;
                    }
                }
                if (verifier == true) {
                    _return = true;
                    verifier = false;
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return rval;
    }

    public void debugAttack(Attack attack) {
        System.out.println("DataHandler::debugAttack() - " + attack.getAttacker() + " dealt " + attack.getAmountOfAttackDamage() + " damage to " + attack.getTarget() + " with " + attack.getAttackName() + ", " + attack.getAttackType() + " " + attack.getAttackClass() + " damage.");
    }
    public long latestTimeStamp = 0;

    long previousStamp = -1;
    public void addData(String line, boolean propagate, boolean parsePrevious, String friendName) {
        String typesToCheck = " 0 1 2 3 6 7 8 9 10 11 14 15 16 17 18 4 5 12 13 19 20 21 22 23 24 25 27 ";
        String tmpLine = line;
        tmpLine = tmpLine.replaceAll("s' ", "s's ");
        //System.out.println(line);
        try {
            int combatType = this.checkForCombatLine(tmpLine, !propagate);
//            System.out.println(combatType + ": " + tmpLine.toString());
            if (combatType > -1 && combatType != -2 && combatType != -3) {
                if (combatType <= 25 || combatType == 27)  {
                    Attack attack = this.getCombatLineInfo(tmpLine, combatType);
//                    if (this.parent.getMainCharacter().equalsIgnoreCase("Moardots")) {
//                        attack.setTimeStamp(attack.getTimeStamp()-deltaOffset);
//                    }

                    long timeStamp = attack.getTimeStamp();
                    long localTime = attack.getLocalTime();
                    if (this.parent.zonewideEncounter == null && !attack.isHeal()) {
                        this.parent.checkForServer();
                        this.parent.zonewideEncounter = new Encounter(attack.getTimeStamp(), this.parent.getMainCharacter(), true);
                    }

                    if (this.currentEncounter == null && !attack.isHeal()) {
                        this.parent.checkForServer();
                        this.currentEncounter = new Encounter(attack, this.parent.getMainCharacter(), this.parent.getCurrentZone(),
                                !propagate, friendName);

                        if (propagate) {
                            if (typesToCheck.contains(" " + combatType + " ")) {
                                this.parent.sendData(attack);
                            }
                        }
                    } else if ((this.watcher != null && !this.watcher.getEncounterActive()) && !attack.isHeal() || (parsePrevious && (timeStamp - previousStamp) >= 3000)) {

                        this.currentEncounter = null;
                        this.parent.checkForServer();
                        this.currentEncounter = new Encounter(attack, this.parent.getMainCharacter(), this.parent.getCurrentZone(),
                                !propagate, friendName);

                        if (propagate) {
                            if (typesToCheck.contains(" " + combatType + " ")) {
//                                if (this.parent.getMainCharacter().equalsIgnoreCase("Moardots")) {
//                                    attack.setLocalTime(localTime-deltaOffset);
//                                }
                                this.parent.sendData(attack);
                            }
                        }
                    } else if (this.currentEncounter != null) {
                        this.currentEncounter.addAttack(attack, !propagate, friendName);
                        if (propagate) {
                            if (typesToCheck.contains(" " + combatType + " ")) {
//                                if (this.parent.getMainCharacter().equalsIgnoreCase("Moardots")) {
//                                    attack.setLocalTime(localTime-deltaOffset);
//                                }
                                this.parent.sendData(attack);
                            }
                        }
                    }

                    if (!parsePrevious) {
                        if (this.watcher == null && !attack.isHeal()) {
                            this.watcher = new EncounterWatcher(this, this.currentEncounter, 5);
                            this.watcher.start();
                        } else if (this.watcher != null && !this.watcher.getEncounterActive() && !attack.isHeal()) {
                            this.watcher = null;
                            this.watcher = new EncounterWatcher(this, this.currentEncounter, 5);
                            this.watcher.start();
                        }
                        long tempStamp = 0;
                        if (this.latestTimeStamp == 0) {
                            this.latestTimeStamp = attack.getTimeStamp();
                        } else {
                            tempStamp = this.latestTimeStamp - attack.getTimeStamp();
                        }
                        if (!attack.isHeal()) {
                            this.watcher.refreshEncounter();
                        }
                    } else {
                        if (previousStamp == -1) {
                            previousStamp = timeStamp;
                        } else {
                            if ((timeStamp - previousStamp) >= 3000) {
                                //this.watcher.forceEncounterEnd();
                                this.parent.addPlaceHolder("Encounter");
                                this.parent.addEncounter(currentEncounter, -1);
                                previousStamp = timeStamp;
                            }
                        }
                    }
                    if ((combatType <= 25 && combatType > 12) && (this.watcher != null) && (this.watcher.getEncounterActive())) {
                        this.watcher.refreshEncounter();
                        if (!attack.isHeal()) {
                            this.addSpellToTimerWindow(attack);
                        }
                    }
                    if ((combatType == 2 || combatType == 27 || combatType == 4) && !attack.isHeal()) {
                        this.addSpellToTimerWindow(attack);
                    }
                }
            } else if (combatType == -2 && !line.contains("into combat by ")) { // ZONE CHANGE
                String zone = line.substring(line.indexOf("You have entered ") + "You have entered ".length(), line.lastIndexOf("."));
                this.parent.requestZoneChange(zone);
            } else if (combatType == -3) { // SOMETHING DIED
                String deadCombatant = tmpLine.substring(tmpLine.indexOf("killed") + "killed ".length(), tmpLine.length() - 1);
                this.parent.checkDeadCombatant(deadCombatant);
            } else if (combatType == -1) { // DO NOT REFRESH THE ENCOUNTER
                if (this.currentEncounter != null && this.watcher != null && this.watcher.getEncounterActive()) {
                    try {
                        long previousTime = this.currentEncounter.getLastUpdated().getTime();
                        long currentTime = Long.parseLong(line.substring(1, 11)) * 1000;
                        int endEnc = Integer.parseInt(this.parent.getProgramProperties().getProperty("secondsToWaitToEndEncounter"));
                        if (currentTime - previousTime > endEnc*1000) {
                            this.watcher.forceEncounterEnd();
                        }
                    } catch (NumberFormatException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        } catch (java.lang.StringIndexOutOfBoundsException e) {
        //e.printStackTrace();
        //System.out.println(line);
        }
    }

    long deltaOffset = 3600000;
    public void addData(Attack attack, String friendName) {
        boolean propagate = false;
        long timeStamp = attack.getTimeStamp();

        if (this.parent.zonewideEncounter == null) {
            this.parent.checkForServer();
            this.parent.zonewideEncounter = new Encounter(attack.getTimeStamp(), (this.parent.getServer().equalsIgnoreCase("battlegrounds") ? this.parent.getServer() + "." : "") + this.parent.getMainCharacter(), true);
        }

        if (this.currentEncounter == null) {

            this.currentEncounter = new Encounter(attack, (this.parent.getServer().equalsIgnoreCase("battlegrounds") ? this.parent.getServer() + "." : "") + this.parent.getMainCharacter(), this.parent.getCurrentZone(),
                    !propagate, friendName);
        } else if (this.watcher != null && !this.watcher.getEncounterActive()) {

            this.currentEncounter = null;
            this.currentEncounter = new Encounter(attack, (this.parent.getServer().equalsIgnoreCase("battlegrounds") ? this.parent.getServer() + "." : "") + this.parent.getMainCharacter(), this.parent.getCurrentZone(),
                    !propagate, friendName);
        } else if (this.currentEncounter != null) {
            this.currentEncounter.addAttack(attack, !propagate, friendName);
        }

        if (this.watcher == null) { //Integer.parseInt(this.parent.getProgramProperties().getProperty("secondsToWaitToEndEncounter"))
            this.watcher = new EncounterWatcher(this, this.currentEncounter, 5);
            this.watcher.start();
        } else if (!this.watcher.getEncounterActive()) {
            this.watcher = null;
            this.watcher = new EncounterWatcher(this, this.currentEncounter, 5);
            this.watcher.start();
        }

        if (this.latestTimeStamp == 0) {
            this.latestTimeStamp = attack.getTimeStamp();
        }
        if (!attack.isHeal() || encounterStartedWithHeal) {
            this.watcher.refreshEncounter();
        }
    }

    public void requestZoneChange(String zone) {
        if ((this.currentEncounter != null) && (this.watcher.getEncounterActive())) {
            this.watcher.stopRunning();
        }
        this.parent.setCurrentLocation(zone);
    }

    public void resetCurrentEncounter() {
        this.currentEncounter = null;
    }

    public void resetCurrentEncounterWatcher() {
        this.watcher = null;
    }

//      CURES CURES CURES CURES CURES
//    (1251739668)[Mon Aug 31 13:27:48 2009] YOUR Cure relieves Disarming Quake from Aditu's unswerving hammer.
//(1251739961)[Mon Aug 31 13:32:41 2009] YOUR Devoted Resolve relieves YOU of Maul.
//One would presume a 3rd party message would look like:
//Soandso's Cure relieves Someone of Something.
//      CURES CURES CURES CURES CURES

    private void initCombatTypes() {

         // something was killed
        combatLines.add(" killed::-3");

        // change zones
        combatLines.add("You have entered ::-2");

        // temporary throwaway damage
        combatLines.add(" hits ;;for ;; and ;; damage::9999");

//        (1280373705)[Wed Jul 28 23:21:45 2010] Azara the Seer's Brutal Inquiry hits Yiix for 422 magic damage.

        //damage was warded
        combatLines.add("YOU ;;aoe attack ;;but fail to inflict any damage::27");
        combatLines.add("YOU ;;double attack ;;but fail to inflict any damage::27");
        combatLines.add("hits YOU;;fails to inflict any damage::28");
        combatLines.add("hits;;fail to inflict any damage::28");
        combatLines.add("hits;;fails to inflict any damage::28");
        combatLines.add("hit;;fail to inflict any damage::28");
        combatLines.add("hit;;fails to inflict any damage::28");
        combatLines.add("critically hits;;fails to inflict any damage::28");
        combatLines.add("critically hits;;fail to inflict any damage::28");

        // (1188866204)[Mon Sep 03 19:36:44 2007] Veeman's Infected Blade hits an undying warrior for 387 disease and 5 slashing damage.

        // outgoing damage
        combatLines.add("YOUR;;hits;;for;;damage::0");
        combatLines.add("YOU hit;;damage::1");
        combatLines.add("YOU critically hit;;damage::1");
        combatLines.add("YOU double attack;;damage::1");
        combatLines.add("YOU critically double attack;;damage::1");
        combatLines.add("YOU aoe attack;;damage::1");
        combatLines.add("YOU critically aoe attack;;damage::1");

        // incoming damage
        combatLines.add("'s;;hits;;YOU ;;damage::2");
        combatLines.add("hits;;YOU ;;damage::3");

        // 3rd party outgoing damage
        combatLines.add("'s;;hits;;damage::4");
        combatLines.add("hits;;damage::5");

    /* HEALING */
        // outgoing healing
        combatLines.add("YOUR;;heals;;YOU ::6");
        combatLines.add("YOUR;;heals;;hit points::7");
        combatLines.add("YOUR;;absorbs;;damage from being done to YOURSELF::8");
        combatLines.add("YOUR;;absorbs;;damage from being done to ::9");

        // incoming healing
        combatLines.add("'s;;heals;;YOU ::10");
        combatLines.add("'s;;absorbs;;from being done to YOU::11");

        // 3rd party healing
        combatLines.add("'s;;heals;;hit points::12");
        combatLines.add("'s;;absorbs;;from being done to::13");

    /* AVOIDANCE */
        // incoming avoided damage
        combatLines.add("tries to;;YOU with;;but misses::14");
        combatLines.add("tries to;;YOU,;;but misses::15");
        combatLines.add("tries to;;YOU with;;but;;YOU resist::16");
        combatLines.add("tries to;;YOU with;;but YOU::17");  // combat art blocked / dodged / parried
        combatLines.add("tries to;;YOU with;;but::17");      // someone else avoided for YOU
        combatLines.add("tries to;;YOU,;;but YOU::18");        // regular attack blocked / dodged / parried
        combatLines.add("tries to;;YOU,;;but::18");            // someone else avoided for YOU

        //Attacker = Maestro Kerberos, Target = Shinzul attack name = Shower of Steel,
        //, attack type = freeze Darkwindmaker with Shower of Steel, but
        //ERROR with attack: timestamp = Sat Sep 29 13:14:47 CDT 2007,
        //attacker = Maestro Kerberos, target = Shinzul, attack name = Shower of Steel,
        //Maestro Kerberos tries to freeze Darkwindmaker with Shower of Steel, but YOU block.

        // 3rd party avoided damage
        combatLines.add("tries to;;with;;but misses::19");
        combatLines.add("tries to;;but misses::20");
        combatLines.add("tries to;;with;;but;;resists::21");
        combatLines.add("tries to;;with;;but::22");           // combat art blocked / dodged / parried
        combatLines.add("tries to;;but::23");                 // regular attack blocked / dodged / parried

        combatLines.add("YOU try to;;with;;but::24");
        combatLines.add("YOU try to;;but::25");

        //power drains
        combatLines.add("draining;;of power::26");
    }

    private Attack getCombatLineInfo(String line, int combatType) {
        Attack rval = new Attack();
        rval.setOriginatingServer(this.parent.getServer());
        long lagBefore = System.currentTimeMillis();
        String attacker = null;
        String target = null;
        String attackName = null;
        String attackType = null;
        String avoidReason = null;

        rval.setTimeStamp(Long.parseLong(line.substring(1, 11)) * 1000);
        rval.setLocalTime(System.currentTimeMillis());
        rval.setMainCharacter(this.parent.getMainCharacter());
        rval.setLine(line);
        //System.out.println(new Date(rval.getTimeStamp()));

        String fileName = this.parent.getFileName();

        int amountOfAttackDamage = 0;
        int criticalOffset = (line.contains("critically ") ? "critically ".length() : 0);
        int attackNameOffset = 0;
        int targetOffset = 0;
        int beginCombatLineOffset = 44;

        attacker = this.parent.getMainCharacter();

        switch (combatType) {
            case 0:
                try {
                    attackNameOffset = line.lastIndexOf("hits") - criticalOffset - 1;

                    attackName = line.substring(beginCombatLineOffset, attackNameOffset);
//                 (1187741025)[Tue Aug 21 19:03:45 2007] YOUR Buckler Reversal hits a mountain coyote for 477 slashing damage.
                    targetOffset = line.lastIndexOf("for") - 1;
                    target = line.substring(attackNameOffset + 6 + criticalOffset, targetOffset);

                    String tmp = line.substring(targetOffset + 5);
                    attackType = tmp.substring(tmp.indexOf(" ") + 1, tmp.lastIndexOf(" "));

                    amountOfAttackDamage = Integer.parseInt(tmp.substring(0, tmp.indexOf(" ")));

                    if (criticalOffset > 0) {
                        rval.setIsCrit(true);
                    }

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackType(attackType);
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 0. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 1:
                try {
//                 (1187741637)[Tue Aug 21 19:13:57 2007] YOU critically hit a Mistmoore guard for 1480 slashing damage.
//                 (1187741637)[Tue Aug 21 19:13:57 2007] YOU critically aoe attack a Mistmoore guard for 1480 slashing damage.
                    if (line.contains("aoe attack")) {
                        rval.setIsAOEAttack(true);
                        targetOffset = line.lastIndexOf("aoe attack ") + "aoe attack ".length();
                    } else if (line.contains("double attack")) {
                        rval.setIsDoubleAttack(true);
                        targetOffset = line.lastIndexOf("double attack ") + "double attack ".length();
                    } else {
                        targetOffset = line.lastIndexOf("hit") + 4;
                    }

                    target = line.substring(targetOffset, line.lastIndexOf("for") - 1);

                    String tmp = line.substring(line.lastIndexOf("for") + 4);
                    attackType = tmp.substring(tmp.indexOf(" ") + 1, tmp.lastIndexOf(" "));

                    amountOfAttackDamage = Integer.parseInt(tmp.substring(0, tmp.indexOf(" ")));

                    if (criticalOffset > 0) {
                        rval.setIsCrit(true);
                    }

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackType(attackType);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 1. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 2:
                try {
 //                 (1179535004)[Fri May 18 19:36:44 2007] a muck toad's Wild Swing critically hits YOU for 3 crushing damage.
 //                 (1179535004)[Fri May 18 19:36:44 2007] a bog faerie's Searing Strike hits YOU for 4 heat damage.
//                 (1187741025)[Tue Aug 21 19:03:45 2007] Imzoz's Revenge's Sebilisan Torment hits YOU for 477 slashing damage.

                    attacker = line.substring(39, line.lastIndexOf("'s"));
                    target = this.parent.getMainCharacter();

                    String tmp = line.substring(line.lastIndexOf("for") + 4);
                    attackType = tmp.substring(tmp.indexOf(" ") + 1, tmp.lastIndexOf(" "));
                    attackName = line.substring(39 + attacker.length() + 3, line.indexOf("hits") - 1 - criticalOffset);

                    amountOfAttackDamage = Integer.parseInt(tmp.substring(0, tmp.indexOf(" ")));

                    if (criticalOffset > 0) {
                        rval.setIsCrit(true);
                    }
                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackType(attackType);
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 2. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 3:
                try {
//                 (1179535004)[Fri May 18 19:36:44 2007] a muck toad critically hits YOU for 3 crushing damage.
//                 (1179535004)[Fri May 18 19:36:44 2007] a muck toad critically double attacks YOU for 3 crushing damage.
                    target = attacker;

                    String tmpType = null;
                    if (line.contains("aoe attacks")) {
                        rval.setIsAOEAttack(true);
                        tmpType = "aoe attacks";
                    } else if (line.contains("double attacks")) {
                        rval.setIsDoubleAttack(true);
                        tmpType = "double attacks";
                    } else {
                        tmpType = "hits";
                    }

                    attacker = line.substring(39, line.indexOf(tmpType) - 1 - criticalOffset);
                    String tmp = line.substring(line.lastIndexOf("for") + 4);
                    attackType = tmp.substring(tmp.indexOf(" ") + 1, tmp.lastIndexOf(" "));

                    amountOfAttackDamage = Integer.parseInt(tmp.substring(0, tmp.indexOf(" ")));

                    if (criticalOffset > 0) {
                        rval.setIsCrit(true);
                    }
                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackType(attackType);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 3. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 4:
                try {
//(1188436091)[Wed Aug 29 20:08:11 2007] a palace protector critically hits Elethorn's blighted horde for 2789 crushing damage.
//(1189133284)[Thu Sep 06 21:48:04 2007] Elethorn's undead tide hits Wuoshi for 452 piercing damage.
//(1179535303)[Fri May 18 19:41:43 2007] Hakujin's Lunge Reversal critically hits a muck toad for 78 piercing damage.
//(1179535303)[Fri May 18 19:41:43 2007] Hakujin's Lunge Reversal critically hits Imzoz's Revenge for 78 piercing damage.
//(1179535303)[Fri May 18 19:41:43 2007] Imzoz's Revenge critically hits Elethorn for 78 piercing damage.
//(1179535303)[Fri May 18 19:41:43 2007] Imzoz's Revenge's Sebilisan Torment critically hits Elethorn for 78 piercing damage.
//(1189133284)[Thu Sep 06 21:48:04 2007] Elethorn's undead tide hits Imzoz's Revenge for 452 piercing damage.
//(1179535303)[Fri May 18 19:41:43 2007] Shinzul hits Imzoz's Revenge for 78 piercing damage.
//(1276572357)[Mon Jun 14 23:25:57 2010] Sykl's ball lightning's Shock critically hits a Scaleborn soulclencher for 537 magic damage.
//(1189132325)[Thu Sep 06 21:32:05 2007] Wuoshi's Breath of Unrestrained Growth hits Farrelle's Spiritual Circle for 76 poison damage.
//(1276572357)[Mon Jun 14 23:25:57 2010] a Scaleborn reaver's Voracious Soul hits Sykl's Ring of Fire for 117 disease damage.
//(1179535303)[Fri May 18 19:41:43 2007] Sykl's ball lightning's shock hits an Advisor's assistant for 520 magic damage.

//(1280373705)[Wed Jul 28 23:21:45 2010] Azara the Seer's Brutal Inquiry hits Yiix for 422 magic damage.
                    
                    attackName = null;

                    if (line.lastIndexOf("'s") > line.indexOf("hits")) {
                        if (line.indexOf("'s") != line.lastIndexOf("'s")) {
                            String tmp1 = line.substring(39, line.indexOf(" hits"));
                            if (tmp1.contains("Imzoz's Revenge")) {
                                attacker = tmp1.substring(0, tmp1.lastIndexOf("'s"));
                            } else {
                                attacker = tmp1.substring(0, tmp1.indexOf("'s"));
                            }

                            target = line.substring(line.indexOf("hits ") + 5, line.lastIndexOf(" for"));
                            attackName = line.substring(line.indexOf("'s") + 3, line.indexOf("hits") - 1 - criticalOffset);
                        } else {
                            attacker = line.substring(39, line.indexOf("hits") - 1 - criticalOffset);
                            target = line.substring(line.indexOf("hits") + 5, line.lastIndexOf("for") - 1);
                        }
                    } else {
                        boolean parsePetDamage = Boolean.parseBoolean(this.parent.getProgramProperties().getProperty("parsePetDamage"));
                        if (!line.contains("Imzoz's Revenge") && parsePetDamage) {
                            String tmp1 = line.substring(39, line.indexOf(" hits "));
                            attacker = tmp1.substring(0, tmp1.indexOf("'s "));
                            target = line.substring(line.indexOf(" hits ") + 6, line.lastIndexOf(" for "));
                            attackName = tmp1.substring(tmp1.indexOf("'s ") + 3, tmp1.length() - criticalOffset);
                        } else if (line.contains("Imzoz's Revenge") || !parsePetDamage) {
                            String tmp1 = line.substring(39, line.indexOf(" hits"));
                            attacker = tmp1.substring(0, tmp1.lastIndexOf("'s"));
                            target = line.substring(line.indexOf("hits ") + 5, line.lastIndexOf(" for"));
                            attackName = tmp1.substring(tmp1.lastIndexOf("'s") + 3, tmp1.length() - criticalOffset);
                        }
                    }

                    String tmp = line.substring(line.lastIndexOf(" for ") + 5);
                    attackType = tmp.substring(tmp.indexOf(" ") + 1, tmp.lastIndexOf(" "));

                    amountOfAttackDamage = Integer.parseInt(tmp.substring(0, tmp.indexOf(" ")));

                    if (attackName != null) {
                        rval.setAttackName(attackName);
                    }

                    if (criticalOffset > 0) {
                        rval.setIsCrit(true);
                    }
                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackType(attackType);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 4. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 5:
                try {
//(1179535303)[Fri May 18 19:41:43 2007] Hakujin critically hits a muck toad for 20 piercing damage.
//(1179535303)[Fri May 18 19:41:43 2007] Hakujin critically aoe attacks a muck toad for 20 piercing damage.

                    String tmpType = null;
                    if (line.contains("aoe attack")) {
                        rval.setIsAOEAttack(true);
                        tmpType = "aoe attacks";
                    } else if (line.contains("double attack")) {
                        rval.setIsDoubleAttack(true);
                        tmpType = "double attacks";
                    } else {
                        tmpType = "hits";
                    }

                    attacker = line.substring(39, line.indexOf(tmpType) - 1 - criticalOffset);
                    target = line.substring(line.indexOf(tmpType) + tmpType.length() + 1, line.lastIndexOf(" for"));

                    String tmp = line.substring(line.lastIndexOf("for ") + 4);
                    attackType = tmp.substring(tmp.indexOf(" ") + 1, tmp.lastIndexOf(" "));

                    amountOfAttackDamage = Integer.parseInt(tmp.substring(0, tmp.indexOf(" ")));

                    if (criticalOffset > 0) {
                        rval.setIsCrit(true);
                    }
                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackType(attackType);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 5. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 6:
                try {
 //                 (1188870568)[Mon Sep 03 20:49:28 2007] YOUR Absolve Vitae critically heals YOU for 205 hit points.
 //                 (1189131861)[Thu Sep 06 21:24:21 2007] YOUR essence of health critically heals YOU for 634 hit points.
                    target = attacker;

                    String tmp = line.substring(line.lastIndexOf("for") + 4);
                    amountOfAttackDamage = Integer.parseInt(tmp.substring(0, tmp.indexOf(" ")));

                    attackName = line.substring(44, line.indexOf("heals") - criticalOffset - 1);

                    if (criticalOffset > 0) {
                        rval.setIsCrit(true);
                    }
                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackTypeInt(Attack.ATTACK_TYPE_NON_MELEE_HEALING);
                    rval.setAttackType("healing");
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 6. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 7:
                try {
 //                 (1188870568)[Mon Sep 03 20:49:28 2007] YOUR Absolve Vitae critically heals Hakujin for 205 hit points.

                    target = line.substring(line.indexOf("heals") + 6, line.indexOf(" for"));

                    String tmp = line.substring(line.lastIndexOf("for") + 4);
                    amountOfAttackDamage = Integer.parseInt(tmp.substring(0, tmp.indexOf(" ")));

                    attackName = line.substring(44, line.indexOf("heals") - criticalOffset - 1);

                    if (criticalOffset > 0) {
                        rval.setIsCrit(true);
                    }
                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackTypeInt(Attack.ATTACK_TYPE_NON_MELEE_HEALING);
                    rval.setAttackType("healing");
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 7. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 8:
                try {
                    //YOUR Carrion Warding absorbs 1293 points of damage from being done to YOURSELF
//ERROR with type 8. Line = (1276730534)[Wed Jun 16 19:22:14 2010] YOUR Runic Redemption absorbs 1 point of damage from being done to YOURSELF.
                    target = attacker;

                    amountOfAttackDamage = Integer.parseInt(line.substring(line.indexOf(" absorbs ") + 9, line.indexOf(" point")));
                    attackName = line.substring(44, line.indexOf(" absorbs "));

                    if (criticalOffset > 0) {
                        rval.setIsCrit(true);
                    }
                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackTypeInt(Attack.ATTACK_TYPE_NON_MELEE_HEALING);
                    rval.setAttackType("healing");
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 8. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 9:
                try {
                    //YOUR Carrion Warding absorbs 1293 points of damage from being done to Deule

                    target = line.substring(line.indexOf(" done to ") + 9, line.length()-1);

                    if (line.contains("point of damage")) {
                        amountOfAttackDamage = Integer.parseInt(line.substring(line.indexOf(" absorbs ") + 9, line.indexOf(" point of damage ")));
                    } else {
                        amountOfAttackDamage = Integer.parseInt(line.substring(line.indexOf(" absorbs ") + 9, line.indexOf(" points of damage ")));
                    }
                    
                    attackName = line.substring(44, line.indexOf(" absorbs "));

                    if (criticalOffset > 0) {
                        rval.setIsCrit(true);
                    }
                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackTypeInt(Attack.ATTACK_TYPE_NON_MELEE_HEALING);
                    rval.setAttackType("healing");
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 9. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 10:
                try {
 //                 (1188870568)[Mon Sep 03 20:49:28 2007] Atlanis's Absolve Vitae critically heals YOU for 205 hit points.
                    target = attacker;
                    attacker = line.substring(39, line.indexOf("'s"));

                    String tmp = line.substring(line.lastIndexOf("for") + 4);
                    amountOfAttackDamage = Integer.parseInt(tmp.substring(0, tmp.indexOf(" ")));

                    attackName = line.substring(line.indexOf("'s") + 3, line.indexOf("heals") - criticalOffset - 1);

                    if (criticalOffset > 0) {
                        rval.setIsCrit(true);
                    }
                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackTypeInt(Attack.ATTACK_TYPE_NON_MELEE_HEALING);
                    rval.setAttackType("healing");
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 10. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 11:
                try {
 //                 (1188870576)[Mon Sep 03 20:49:36 2007] Farrelle's Carrion Bulwark absorbs 3582 points of damage from being done to YOU.
                    target = attacker;
                    attacker = line.substring(39, line.indexOf("'s"));

                    String tmp = line.substring(line.lastIndexOf("absorbs") + 8);
                    amountOfAttackDamage = Integer.parseInt(tmp.substring(0, tmp.indexOf(" ")));

                    attackName = line.substring(line.indexOf("'s") + 3, line.indexOf("absorbs") - criticalOffset - 1);

                    if (criticalOffset > 0) {
                        rval.setIsCrit(true);
                    }
                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackTypeInt(Attack.ATTACK_TYPE_NON_MELEE_HEALING);
                    rval.setAttackType("healing");
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 11. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 12:
                try {
//ERROR with type 12. Line = (1279429189)[Sun Jul 18 00:59:49 2010] a forgotten custodian's Thuri's Doleful Thrust heals a forgotten custodian for 318 hit points.
 //                 (1188870576)[Mon Sep 03 20:49:36 2007] Atlanis's Supplicant's Prayer heals Ocrana for 392 hit points.
                    attacker = line.substring(39, line.indexOf("'s "));
                    target = line.substring(line.indexOf(" heals ") + 7, line.indexOf(" for "));

                    String tmp = line.substring(line.lastIndexOf(" for ") + 5);
                    amountOfAttackDamage = Integer.parseInt(tmp.substring(0, tmp.indexOf(" ")));

                    attackName = line.substring(line.indexOf("'s") + 3, line.indexOf(" heals ") - criticalOffset);

                    if (criticalOffset > 0) {
                        rval.setIsCrit(true);
                    }
                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackTypeInt(Attack.ATTACK_TYPE_NON_MELEE_HEALING);
                    rval.setAttackType("healing");
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 12. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 13:
                try {
//(1188870589)[Mon Sep 03 20:49:49 2007] Farrelle's Carrion Bulwark absorbs 1642 points of damage from being done to Ocrana.
//(1189132324)[Thu Sep 06 21:32:04 2007] Farrelle's Spiritual Circle's Spiritual Circle absorbs 596 points of damage from being done to Sykl.
//(1188880359)[Mon Sep 03 23:32:39 2007] Farrelle's Curse of Shielding absorbs 865 points of damage from being done to Nitestorm's protoferno.
                    
//(1279845906)[Thu Jul 22 20:45:06 2010] Guilhem's Elemental Concerto absorbs 184 points of damage from being done to Zanshen.
                    if (line.lastIndexOf("'s") > line.indexOf("'s")) {
                        if (line.lastIndexOf("'s") > line.indexOf("absorbs")) {
                            attacker = line.substring(39, line.indexOf("'s"));
                        } else {
                            attacker = line.substring(39, line.lastIndexOf("'s"));
                        }
                    } else {
                        attacker = line.substring(39, line.indexOf("'s"));
                    }

                    target = line.substring(line.indexOf(" to ") + 4, line.length() - 1);

                    String tmp = line.substring(line.lastIndexOf("absorbs") + 8);
                    amountOfAttackDamage = Integer.parseInt(tmp.substring(0, tmp.indexOf(" ")));

                    attackName = line.substring(line.indexOf("'s") + 3, line.indexOf("absorbs") - criticalOffset - 1);

                    if (criticalOffset > 0) {
                        rval.setIsCrit(true);
                    }
                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackTypeInt(Attack.ATTACK_TYPE_NON_MELEE_HEALING);
                    rval.setAttackType("healing");
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 13. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 14:
                try {
//(1179535303)[Fri May 18 19:41:43 2007] a muck toad tries to slash YOU with Wild Swing, but misses.
//(1190079074)[Mon Sep 17 20:31:14 2007] a seedling storyteller tries to slash YOU with Singing Strike, but misses.

                    attacker = line.substring(39, line.indexOf("tries") - 1);
                    target = this.parent.getMainCharacter();

                    String tmp = line.substring(line.lastIndexOf("to ") + 3);
                    attackType = tmp.substring(0, tmp.lastIndexOf(" YOU"));

                    attackName = tmp.substring(tmp.lastIndexOf("with") + 5, tmp.lastIndexOf(","));

                    amountOfAttackDamage = 0;

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAvoided(true, "miss");
                    rval.setAttackType(attackType);
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 14. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 15:
                try {
 //                 (1179535303)[Fri May 18 19:41:43 2007] a muck toad tries to slash YOU, but misses.
 //                 (1276132309)[Wed Jun 09 21:11:49 2010] a silent sentinel tries to crush YOU, but misses.
                    attacker = line.substring(39, line.indexOf("tries") - 1);
                    target = this.parent.getMainCharacter();

                    String tmp = line.substring(line.lastIndexOf("to ") + 3);
                    attackType = tmp.substring(0, tmp.lastIndexOf(" YOU"));

                    amountOfAttackDamage = 0;

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAvoided(true, "miss");
                    rval.setAttackType(attackType);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 15. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 16:
                try {
//(1189132401)[Thu Sep 06 21:33:21 2007] Wuoshi tries to smite YOU with Stinging Insects, but YOU resist.
//(1281022542)[Fri May 18 19:31:08 2007] Ecto Malgum tries to smite YOU with Soul Fetters, but YOU resist.
                    attacker = line.substring(39, line.indexOf(" tries "));
                    target = this.parent.getMainCharacter();

                    String tmp = line.substring(line.lastIndexOf(" with ") + 6);
                    attackName = tmp.substring(0, tmp.indexOf(","));

                    attackType = line.substring(line.indexOf(" to ") + 4, line.indexOf(" YOU "));
                    amountOfAttackDamage = 0;

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackName(attackName);
                    rval.setAttackType(attackType);

                    rval.setAvoided(true, "resist");
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 16. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 17:
                try {
//(1189132346)[Thu Sep 06 21:32:26 2007] Wuoshi tries to slash YOU with Frenzied Blows, but YOU parry.
//(1189131870)[Thu Sep 06 21:24:30 2007] Wuoshi tries to slash YOU, but Aditu parries.
//(1276132310)[Wed Jun 09 21:11:50 2010] a silent sentinel tries to crush YOU with Berserker Onslaught, but YOU block.
                    attacker = line.substring(39, line.indexOf(" tries "));
                    target = this.parent.getMainCharacter();

                    String tmp = line.substring(line.lastIndexOf(" with ") + 6);
                    attackName = tmp.substring(0, tmp.indexOf(", but "));

                    attackType = line.substring(line.indexOf(" to ") + 4, line.indexOf(" YOU "));

                    amountOfAttackDamage = 0;

                    avoidReason = line.substring(line.lastIndexOf(" ") + 1, line.length() - 1);

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackName(attackName);
                    rval.setAttackType(attackType);
                    rval.setAvoided(true, avoidReason);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 17. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 18:
                try {
//(1189132352)[Thu Sep 06 21:32:32 2007] Wuoshi tries to slash YOU, but YOU block.
//(1189566771)[Tue Sep 11 22:12:51 2007] a totemic footsoldier tries to crush Ocrana, but YOU block.
//(1189131870)[Thu Sep 06 21:24:30 2007] Wuoshi tries to slash YOU, but Aditu parries.
//(1276132564)[Wed Jun 09 21:16:04 2010] a silent sentinel tries to crush YOU, but YOU block.
//(1276132310)[Wed Jun 09 21:11:50 2010] a silent sentinel tries to smite YOU with Berserker Onslaught, but YOU resist.

//ERROR with type 23. Line = (1276733021)[Wed Jun 16 20:03:41 2010] a silent sentinel tries to crush Sykl's ball lightning, but their aoe attack misses.
//ERROR with type 23. Line = (1276733068)[Wed Jun 16 20:04:28 2010] Featherweight tries to crush a herald of Dar, but their double attack misses.
//ERROR with type 23. Line = (1276733508)[Wed Jun 16 20:11:48 2010] Lail tries to pierce a silent sentinel, but a silent sentinel parries the aoe attack.
//ERROR with attack: timestamp = Wed Jun 16 20:26:59 EDT 2010, attacker = Shinzul, target = a Scaleborn acolyte, attack name = Deadly Invective, attack type = disease
//ERROR with attack: timestamp = Wed Jun 16 20:27:04 EDT 2010, attacker = Shinzul, target = a Scaleborn acolyte, attack name = Berserker Onslaught, attack type = slashing
//ERROR with type 1. Line = (1276741668)[Wed Jun 16 22:27:48 2010] YOU aoe attack a Scaleborn banisher but fail to inflict any damage.
//ERROR with attack: timestamp = Wed Jun 16 22:31:06 EDT 2010, attacker = a Scaleborn ritualist, target = Shinzul, attack name = Invocation, attack type = smite
//ERROR with attack: timestamp = Wed Jun 16 22:31:07 EDT 2010, attacker = Shinzul, target = a Scaleborn ritualist, attack name = Acceleration Strike, attack type = slashing
//ERROR with attack: timestamp = Wed Jun 16 22:31:07 EDT 2010, attacker = Shinzul, target = a Scaleborn ritualist, attack name = Rampaging Blow, attack type = slashing
//ERROR with type 18. Line = (1276731831)[Wed Jun 16 19:43:51 2010] a hungry gorg tries to slash YOU, but their aoe attack misses.

                    attacker = line.substring(39, line.indexOf(" tries"));
                    target = this.parent.getMainCharacter();

                    String tmp = line.substring(line.indexOf(" to ") + 4).replaceAll(" the aoe attack", "").replaceAll(" the double attack", "").replaceAll(" the flurry attack", "");
                    avoidReason = tmp.substring(tmp.lastIndexOf(" ") + 1, tmp.length() - 1);
                    amountOfAttackDamage = 0;
                    
                    attackType = tmp.substring(0, tmp.indexOf(" "));

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackType(attackType);
                    rval.setAvoided(true, avoidReason);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 18. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 19:
                try {
//(1189132163)[Thu Sep 06 21:29:23 2007] Jonwa tries to pierce Wuoshi with Flailing Centipede, but misses.
                    attacker = line.substring(39, line.indexOf("tries") - 1);

                    String tmp = line.substring(line.indexOf("to ") + 3);
                    attackType = tmp.substring(0, tmp.indexOf(" "));

                    target = tmp.substring(tmp.indexOf(" ") + 1, tmp.indexOf(" with"));

                    attackName = line.substring(line.lastIndexOf("with") + 5, line.lastIndexOf(","));

                    amountOfAttackDamage = 0;

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackType(attackType);
                    rval.setAvoided(true, "miss");
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 19. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 20:
                try {
//(1189132351)[Thu Sep 06 21:32:31 2007] Cort tries to slash Wuoshi, but misses.
//(1189132581)[Thu Sep 06 21:36:21 2007] Nitestorm's protoferno tries to burn Wuoshi, but misses.
                    attacker = line.substring(39, line.indexOf("tries") - 1);

                    String tmp = line.substring(line.indexOf("to ") + 3);
                    attackType = tmp.substring(0, tmp.indexOf(" "));

                    target = tmp.substring(tmp.indexOf(" ") + 1, tmp.indexOf(","));

                    amountOfAttackDamage = 0;

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackType(attackType);
                    rval.setAvoided(true, "miss");
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 20. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 21:
                try {
//(1189132371)[Thu Sep 06 21:32:51 2007] Dizzco tries to disease Wuoshi with Lanet's Violent Scream, but Wuoshi resists.
//ERROR with type 21. Line = (1189132371)[Thu Sep 06 21:32:51 2007] Sykl's ball lightning tries to zap Wuoshi with ball lightning's Shock, but Wuoshi resists.
                    attacker = line.substring(39, line.indexOf("tries ") - 1);

                    String tmp = line.substring(line.indexOf("to ") + 3);
                    attackType = tmp.substring(0, tmp.indexOf(" "));

                    target = tmp.substring(tmp.indexOf(" ") + 1, tmp.indexOf(" with"));
                    attackName = line.substring(line.lastIndexOf("with ") + 5, line.lastIndexOf(","));

                    amountOfAttackDamage = 0;

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackType(attackType);
                    rval.setAvoided(true, "resist");
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 21. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 22:
                try {
//(1189132351)[Thu Sep 06 21:32:31 2007] Jonwa tries to pierce Wuoshi with Silencing Palm, but misses.
//(1281019421)[Fri May 18 19:31:08 2007] Ecto Malgum tries to crush Battery with Ectoplasmic Blast, but Battery blocks
                    attacker = line.substring(39, line.indexOf(" tries "));

                    String tmp = line.substring(line.indexOf(" to ") + 4);
                    attackType = tmp.substring(0, tmp.indexOf(" "));

                    target = tmp.substring(tmp.indexOf(" ") + 1, tmp.indexOf(" with "));
                    attackName = line.substring(line.lastIndexOf(" with ") + 6, line.lastIndexOf(", "));

                    if (line.contains(" misses.")) {
                        avoidReason = "miss";
                    } else {
                        avoidReason = tmp.substring(tmp.lastIndexOf(" ") + 1, tmp.length() - 1);
                    }

                    amountOfAttackDamage = 0;

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackType(attackType);
                    rval.setAvoided(true, avoidReason);
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 22. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 23:
                try {
//(1189132167)[Thu Sep 06 21:29:27 2007] Jonwa tries to pierce Wuoshi, but Wuoshi parries.
//(1189132576)[Thu Sep 06 21:36:16 2007] Nitestorm's protoferno tries to burn Wuoshi, but Wuoshi parries.
//(1189132724)[Thu Sep 06 21:38:44 2007] Wuoshi tries to slash Nitestorm's protoferno, but Nitestorm's protoferno deflects.
                    attacker = line.substring(39, line.indexOf(" tries "));

                    String tmp = line.substring(line.indexOf(" to ") + 4).replaceAll(" the aoe attack", "").replaceAll(" the double attack", "").replaceAll(" the flurry attack", "");
                    attackType = tmp.substring(0, tmp.indexOf(" "));

                    target = tmp.substring(tmp.indexOf(" ") + 1, tmp.indexOf(","));
                    avoidReason = tmp.substring(tmp.lastIndexOf(" ") + 1, tmp.length() - 1);

                    amountOfAttackDamage = 0;

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackType(attackType);
                    rval.setAvoided(true, avoidReason);
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 23. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 24:
                try {
//(1189126859)[Thu Sep 06 20:00:59 2007] YOU try to slash a bloom custodian with Violent Pledge, but a bloom custodian parries.
//(1189128079)[Thu Sep 06 20:21:19 2007] YOU try to confound Wuoshi with Berate, but Wuoshi resists.
//ERROR with type 24. Line = (1276049703)[Tue Jun 08 22:15:03 2010] YOU try to slash a Scaleborn soulclencher with Rampaging Blow, but miss.
//ERROR with type 24. Line = (1280885719)[Tue Aug 03 21:35:19 2010] YOU try to slash Zanshen with Rupture, but Aditu blocks.
  
                    String tmp = line.substring(line.indexOf(" to ") + 4);
                    attackType = tmp.substring(0, tmp.indexOf(" "));

                    target = tmp.substring(tmp.indexOf(" ") + 1, tmp.indexOf(" with "));
                    attackName = tmp.substring(tmp.indexOf(" with ") + 6, tmp.lastIndexOf(", "));
                    if (line.contains(" miss.")) {
                        avoidReason = "miss";
                    } else {
                        avoidReason = tmp.substring(tmp.lastIndexOf(target) + target.length() + 1, tmp.length() - 1);
                    }
                    amountOfAttackDamage = 0;

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackType(attackType);
                    rval.setAvoided(true, avoidReason);
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 24. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 25:
                try {
//(1189132167)[Thu Sep 06 21:29:27 2007] You try to slash Wuoshi, but Wuoshi parries.
//(1277569040)[Sat Jun 26 12:17:20 2010] YOU try to slash a Blacktalon workshop warden, but a Blacktalon workshop warden blocks the aoe attack.
//ERROR with type 25. Line = (1277953537)[Wed Jun 30 23:05:37 2010] YOU try to slash a Naar'Yora philosopher, but a Naar'Yora philosopher blocks the aoe attack.
                    String tmp = line.substring(line.indexOf("to ") + 3).replaceAll(" the aoe attack", "").replaceAll(" the double attack", "").replaceAll(" the flurry attack", "");
//                    slash a Naar'Yora philosopher, but a Naar'Yora philosopher blocks.
                    attackType = tmp.substring(0, tmp.indexOf(" "));

                    target = tmp.substring(tmp.indexOf(" ") + 1, tmp.indexOf(", "));
                    avoidReason = tmp.substring(tmp.lastIndexOf(" ") + 1, tmp.length() - 1);

                    amountOfAttackDamage = 0;

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackType(attackType);
                    rval.setAvoided(true, avoidReason);
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 25. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 26:
                try {
//(1189131914)[Thu Sep 06 21:25:14 2007] Staven's Vicious Torment confounds Wuoshi draining 1 101 mental of power.
//(1189131914)[Thu Sep 06 21:25:14 2007] Staven's Vicious Torment confounds Imzoz's Revenge draining 1 101 mental of power.
//(1189131914)[Thu Sep 06 21:25:14 2007] Vampire Lord Mayong Mistmoore zap YOU draining 1 101 mental of power.
                    String[] types = {" confound", " confounds", " poison", " poisons"
                            , " freeze", " freezes", " zap", " zaps", " disease", " diseases"
                            , " smite", " smites", " burn", " burns", " crush", " crushes"
                            , " pierce", " pierces", " slash", " slashes"};
                    String tmpType = null;
                    for (int x = 0; x < types.length; x++) {
                        if (line.contains(types[x])) {
                            tmpType = types[x].trim();
                        }
                    }

                    boolean goofyGrammar = false;
                    if (line.contains("'s")) {
                        attacker = line.substring(39, line.indexOf("'s"));
                    } else {
                        goofyGrammar = true;
//                        if (tmpType.equalsIgnoreCase("crushes")) {
//                            tmpType = "crush";
//                        } else if (tmpType.equalsIgnoreCase("slashes")) {
//                            tmpType = "slash";
//                        } else {
//                            tmpType = tmpType.substring(0, tmpType.length() - 1);
//                        }
                        attacker = line.substring(39, line.indexOf(" " + tmpType));
                    }

                    String tmp = line.substring(line.indexOf(tmpType) + tmpType.length() + 1);

                    if (line.contains(" YOU")) {
                        target = this.parent.getMainCharacter();
                    } else {
                        target = tmp.substring(0, tmp.indexOf(" "));
                    }

                    attackType = tmpType;

                    if (!goofyGrammar) {
                        attackName = line.substring(line.indexOf("'s") + 3, line.indexOf(" " + tmpType));
                    }

                    amountOfAttackDamage = 0;

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackType(attackType);
                    if (!goofyGrammar) {
                        rval.setAttackName(attackName);
                    }
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 26. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 27:
                try {
//(1280373696)[Wed Jul 28 23:21:36 2010] Azara the Seer hits Castdogg but fails to inflict any damage.
//(1280373696)[Wed Jul 28 23:21:36 2010] Azara the Seer's Dark Wind hits Jalin but fails to inflict any damage.
//(1281493938)[Tue Aug 10 22:32:18 2010] YOU critically hit a fungal spore but fail to inflict any damage.
                    attacker = this.parent.getMainCharacter();
                    
                    rval.setIsAOEAttack(line.contains(" aoe attack "));
                    rval.setIsDoubleAttack(line.contains(" double attack "));
                    rval.setIsCrit(line.contains(" critically "));
                    
                    line = line.replaceAll(" double attack", "").replaceAll(" aoe attack", "");
                    if (line.contains(" YOUR ")) {
                        attackName = line.substring(line.indexOf(" YOUR ") + 6, line.indexOf(" hits ") - criticalOffset);
                        target = line.substring(line.indexOf(" hits ") + 6, line.indexOf(" but fails to "));
                    } else {
                        target = line.substring(line.indexOf(" hit ") + 5, line.indexOf(" but fail to "));
                    }

                    amountOfAttackDamage = 0;

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 27. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            case 28:
                try {
//(1280373696)[Wed Jul 28 23:21:36 2010] Azara the Seer hits Castdogg but fails to inflict any damage.
//(1280373696)[Wed Jul 28 23:21:36 2010] Azara the Seer's Dark Wind hits Jalin but fails to inflict any damage.
                    if (!line.contains("'s ")) {
                        attacker = line.substring(39, line.indexOf(" hits ") - criticalOffset);
                    } else {
                        attacker = line.substring(39, line.indexOf("'s "));
                        attackName = line.substring(line.indexOf("'s ") + 3, line.indexOf(" hits "));
                    }

                    rval.setIsAOEAttack(line.contains(" aoe attack "));
                    rval.setIsDoubleAttack(line.contains(" double attack "));
                    rval.setIsCrit(line.contains(" critically "));
                    line = line.replaceAll(" double attack", "").replaceAll(" aoe attack", "");

                    target = line.substring(line.indexOf(" hits ") + 6, line.indexOf(" but fails to "));

                    amountOfAttackDamage = 0;

                    rval.setAmountOfAttackDamage(amountOfAttackDamage);
                    rval.setAttackName(attackName);
                    rval.setTarget(target);
                    rval.setAttacker(attacker);
                } catch (Exception e) {
                    System.out.println("ERROR with type 28. Line = " + line);
                    e.printStackTrace();
                    //System.exit(1);
                }
                break;
            default:
                break;
        }
//        rval.setLagTime(System.currentTimeMillis() - lagBefore);
        return rval;
    }
}